﻿using System;
using System.Collections;
#if WINDOWS_PHONE
using StyleMVVM.Data.Collections;
#else
using System.Collections.Concurrent;
#endif
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;
using StyleMVVM.Data;
using StyleMVVM.Data.Activation;
using StyleMVVM.Logging;
#if !DOT_NET
using Windows.Foundation.Metadata;
#else
using System.IO;

#endif

namespace StyleMVVM.DependencyInjection.Impl
{
	/// <summary>
	/// This is the default implementation of IDependencyInjectionContainer.
	/// </summary>
	public sealed class DependencyInjectionContainer : IDependencyInjectionContainer,
																		IContainerBaseExportConfigurationAware
#if DOT_NET
, IServiceProvider
#endif

	{
		private static readonly string supplemental = typeof(DependencyInjectionContainer).FullName;
		private static readonly MethodInfo createLazyGenericMethod;
		private static readonly MethodInfo createReadonlyCollectionMethod;
		private static readonly MethodInfo getAllExportsMethod;

		private volatile Dictionary<string, ExportCollection> activationStrategies;
		private List<IExportPipelineHandler> pipelineHandlers = new List<IExportPipelineHandler>();
		private readonly object activationStrategiesLock = new object();
		private readonly ConcurrentBag<IExportResolver> externalResolvers;
		private readonly HashSet<Type> registeredTypes = new HashSet<Type>();
		private readonly ConcurrentBag<string> blackList;
		private readonly ConcurrentBag<IBaseExportConfiguration> exportConfigurations;
		private readonly ConcurrentBag<IExportAssemblyConfiguration> exportAssemblyConfigurations;
		private readonly ConcurrentDictionary<IExportStrategyFactory, bool> strategyFactories;
		private ConcurrentDictionary<ExportConditionValueProviderDelegate, bool> globalConditions;

		private IDefaultDisposalScopeProvider disposalScopeProvider;
		private IActivationService activationService;
		private ExportEnvironment environment = ExportEnvironment.RunTime;
		private List<IExportStrategyInspector> inspectors = new List<IExportStrategyInspector>();

		static DependencyInjectionContainer()
		{
			createLazyGenericMethod = typeof(DependencyInjectionContainer).GetTypeInfo().GetDeclaredMethod("CreateLazyGeneric");

			createReadonlyCollectionMethod =
				typeof(DependencyInjectionContainer).GetTypeInfo().GetDeclaredMethod("CreateReadonlyCollection");

			getAllExportsMethod = typeof(DependencyInjectionContainer).GetTypeInfo().GetDeclaredMethod("GetAllExports");
		}

		/// <summary>
		/// Default Constructor for the DI container
		/// </summary>
		public DependencyInjectionContainer()
		{
			activationStrategies = new Dictionary<string, ExportCollection>();
			externalResolvers = new ConcurrentBag<IExportResolver>();
			blackList = new ConcurrentBag<string>();
			exportConfigurations = new ConcurrentBag<IBaseExportConfiguration>();
			exportAssemblyConfigurations = new ConcurrentBag<IExportAssemblyConfiguration>();
			strategyFactories = new ConcurrentDictionary<IExportStrategyFactory, bool>();

			AutoRegisterUnknown = true;
		}

		public bool AutoRegisterUnknown { get; set; }

		public ExportEnvironment Environment
		{
			get { return environment; }
			set
			{
				if (environment == ExportEnvironment.DesignTime ||
					 environment == ExportEnvironment.RunTime ||
					 environment == ExportEnvironment.UnitTest)
				{
					environment = value;
				}
				else
				{
					throw new ArgumentException("Environment can only be (DesignTime, RunTime, or UnitTest)");
				}
			}
		}

		public IActivationService ActivationService
		{
			get { return activationService; }
		}

		public void Start()
		{
			Start(true);
		}

		public void Start(bool launchUI)
		{
			Logger.Info("Container starting", supplemental);

			ProcessRegistration(launchUI);
		}

		public IInjectionContext CreateInjectionContext()
		{
			InjectionContext injectionContext = new InjectionContext();

			if (disposalScopeProvider != null)
			{
				injectionContext.DisposalScope = disposalScopeProvider.ProvideDisposalScope();
			}

			if (globalConditions != null)
			{
				foreach (KeyValuePair<ExportConditionValueProviderDelegate, bool> globalCondition in globalConditions)
				{
					KeyValuePair<string, object>? valuePair = globalCondition.Key(this);

					if (valuePair.HasValue)
					{
						injectionContext.SetConditionValue(valuePair.Value.Key, valuePair.Value.Value);
					}
				}
			}

			return injectionContext;
		}

		public IInjectionContext CreateInjectionContext(IDisposalScope disposalScope)
		{
			InjectionContext injectionContext = new InjectionContext();

			injectionContext.DisposalScope = disposalScope;

			if (globalConditions != null)
			{
				foreach (KeyValuePair<ExportConditionValueProviderDelegate, bool> globalCondition in globalConditions)
				{
					KeyValuePair<string, object>? valuePair = globalCondition.Key(this);

					if (valuePair.HasValue)
					{
						injectionContext.SetConditionValue(valuePair.Value.Key, valuePair.Value.Value);
					}
				}
			}

			return injectionContext;
		}

		public IDisposableInjectionContext CreateDisposableInjectionContext()
		{
			DisposableInjectionContext injectionContext;

			if (disposalScopeProvider != null)
			{
				injectionContext = new DisposableInjectionContext(disposalScopeProvider.ProvideDisposalScope());
			}
			else
			{
				injectionContext = new DisposableInjectionContext();
			}

			if (globalConditions != null)
			{
				foreach (KeyValuePair<ExportConditionValueProviderDelegate, bool> globalCondition in globalConditions)
				{
					KeyValuePair<string, object>? valuePair = globalCondition.Key(this);

					if (valuePair.HasValue)
					{
						injectionContext.SetConditionValue(valuePair.Value.Key, valuePair.Value.Value);
					}
				}
			}

			return injectionContext;
		}

		public IDisposableInjectionContext CreateDisposableInjectionContext(IDisposalScope disposalScope)
		{
			DisposableInjectionContext injectionContext = new DisposableInjectionContext();

			injectionContext.DisposalScope = disposalScope;

			if (globalConditions != null)
			{
				foreach (KeyValuePair<ExportConditionValueProviderDelegate, bool> globalCondition in globalConditions)
				{
					KeyValuePair<string, object>? valuePair = globalCondition.Key(this);

					if (valuePair.HasValue)
					{
						injectionContext.SetConditionValue(valuePair.Value.Key, valuePair.Value.Value);
					}
				}
			}

			return injectionContext;
		}

		public void AddCondition(ExportConditionProviderDelegate exportCondition)
		{
			AddConditionValue(x =>
										{
											string newCondition = exportCondition(x);

											if (newCondition != null)
											{
												return new KeyValuePair<string, object>(newCondition, true);
											}

											return null;
										});
		}

		public void AddConditionValue(ExportConditionValueProviderDelegate exportConditionValue)
		{
			if (globalConditions == null)
			{
				globalConditions = new ConcurrentDictionary<ExportConditionValueProviderDelegate, bool>();
			}

			globalConditions[exportConditionValue] = true;
		}

		public void RemoveConditionValue(ExportConditionValueProviderDelegate exportConditionValue)
		{
			if (globalConditions != null)
			{
				bool outValue;

				globalConditions.TryRemove(exportConditionValue, out outValue);
			}
		}

		public void Launch()
		{
			IInjectionContext injectionContext = CreateInjectionContext();

			foreach (IExportStrategy exportStrategy in GetAllStrategies())
			{
				if (exportStrategy.IsCoreExport && exportStrategy.CoreExportStartOrder <= (int)CoreExportStartOrder.UI)
				{
					try
					{
						ActivateObject<object>(injectionContext, exportStrategy, null);

						Logger.Debug("Activated Core Type: " + exportStrategy.ActivationType.FullName, supplemental);
					}
					catch (Exception exp)
					{
						Logger.Error("Exception thrown while creating core service", supplemental, exp);
					}
				}
			}
		}

		public void Shutdown()
		{
			foreach (KeyValuePair<string, ExportCollection> activationStrategy in activationStrategies)
			{
				foreach (IExportStrategy exportStrategy in activationStrategy.Value.ExportStrategies())
				{
					if (exportStrategy.LifeCycleContainer != null)
					{
						try
						{
							exportStrategy.LifeCycleContainer.Shutdown();
						}
						catch (Exception)
						{
						}
					}
				}
			}

			activationStrategies.Clear();

			foreach (IExportStrategyInspector exportStrategyInspector in inspectors)
			{
				exportStrategyInspector.Shutdown();
			}
		}

		public void Inject(object injectionObject)
		{
			if (injectionObject == null)
			{
				throw new ArgumentNullException("injectionObject");
			}

			activationService.Inject(CreateInjectionContext(), injectionObject);
		}

		/// <summary>
		/// Gets the service object of the specified type.
		/// </summary>
		/// <returns>
		/// A service object of type <paramref name="serviceType"/>.-or- null if there is no service object of type <paramref name="serviceType"/>.
		/// </returns>
		/// <param name="serviceType">An object that specifies the type of service object to get. </param><filterpriority>2</filterpriority>
		public object GetService(Type serviceType)
		{
			return LocateByType(serviceType);
		}

		public object Locate(string exportName, params object[] constructorParams)
		{
			return LocateWithContext(CreateInjectionContext(), exportName, constructorParams);
		}

		public object LocateWithContext(IInjectionContext injectionContext,
												  string exportName,
												  params object[] constructorParams)
		{
			if (string.IsNullOrEmpty(exportName))
			{
				throw new ArgumentNullException("exportName", "cannot be null");
			}

			object localExport = injectionContext.LocateExport(exportName, this, constructorParams);

			if (localExport != null)
			{
				return localExport;
			}

			ExportCollection strategies;

			if (activationStrategies.TryGetValue(exportName, out strategies))
			{
				IExportStrategy activationStrategy = FindExportStrategyThatMeetsConditions(injectionContext, strategies.ExportStrategies());

				if (activationStrategy != null)
				{
					return ActivateObject<object>(injectionContext, activationStrategy, constructorParams);
				}
			}

			if (externalResolvers.Count > 0)
			{
				foreach (IExportResolver externalResolver in externalResolvers)
				{
					try
					{
						bool found;

						object returnValue = externalResolver.GetExport(exportName, null, out found, constructorParams);

						if (found)
						{
							return returnValue;
						}
					}
					catch (Exception exp)
					{
						Logger.Error("Exception thrown while calling external resolver", supplemental, exp);
					}
				}
			}

			IEnumerable<IExportStrategy> exportStrategies = RaiseUnknownExport(exportName, null, false, injectionContext);

			if (exportStrategies != null)
			{
				IExportStrategy returnStrategy = FindExportStrategyThatMeetsConditions(injectionContext, exportStrategies);

				if (returnStrategy != null)
				{
					return ActivateObject<object>(injectionContext, returnStrategy, constructorParams);
				}
			}

			return null;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="exportName"></param>
		/// <param name="exportType"></param>
		/// <param name="resolveMultiple"></param>
		/// <param name="injectionContext"></param>
		/// <returns></returns>
		private IEnumerable<IExportStrategy> RaiseUnknownExport(string exportName, Type exportType, bool resolveMultiple, IInjectionContext injectionContext)
		{
			UnknownExportArgs args = new UnknownExportArgs(exportName, exportType, resolveMultiple);

			if (ResolveUnknownExport != null)
			{
				ResolveUnknownExport(this, args);

				if (args.ExportStrategies != null)
				{
					List<IExportStrategy> exportStrategies = new List<IExportStrategy>(args.ExportStrategies);

					SortActivationList(exportStrategies);

					foreach (IExportStrategy exportStrategy in exportStrategies)
					{
						AddStrategy(exportStrategy);
					}

					return exportStrategies;
				}
			}

			return null;
		}

		public object LocateByType(Type exportType, params object[] constructorParams)
		{
			if (exportType == null)
			{
				throw new ArgumentNullException("exportType", "cannot be null");
			}

			return InternalLocate(CreateInjectionContext(), exportType, constructorParams);
		}

		public object LocateByTypeWithContext(IInjectionContext injectionContext,
														  Type exportType,
														  params object[] constructorParams)
		{
			if (exportType == null)
			{
				throw new ArgumentNullException("exportType", "cannot be null");
			}

			return InternalLocate(injectionContext, exportType, constructorParams);
		}

		[DefaultOverload]
		public object LocateByType(TypeWrapper exportType, params object[] constructorParams)
		{
			return LocateByType(exportType.Type, constructorParams);
		}

		public IEnumerable<object> LocateAllByType(Type exportType)
		{
			if (exportType == null)
			{
				throw new ArgumentNullException("exportType", "cannot be null");
			}

			List<object> returnValues = new List<object>();
			IInjectionContext injectionContext = CreateInjectionContext();

			GetAllExports<object>(injectionContext, exportType.FullName, returnValues, null);

			return returnValues;
		}

		public IEnumerable<object> LocateAllByTypeWithContext(IInjectionContext injectionContext, Type exportType)
		{
			if (exportType == null)
			{
				throw new ArgumentNullException("exportType", "cannot be null");
			}

			List<object> returnValues = new List<object>();

			GetAllExports<object>(injectionContext, exportType.FullName, returnValues, null);

			return returnValues;
		}

		public IEnumerable LocateAllByTypeWithContext(IInjectionContext injectionContext, Type exportType, IList outList)
		{
			GetAllExports<object>(injectionContext, exportType.FullName, outList, null);

			return outList;
		}

		[DefaultOverload]
		public IEnumerable<object> LocateAllByType(TypeWrapper exportType)
		{
			return LocateAllByType(exportType.Type);
		}

		public IEnumerable<object> LocateAll(string exportName)
		{
			if (string.IsNullOrEmpty(exportName))
			{
				throw new ArgumentNullException("exportName", "cannot be null");
			}

			List<object> returnValues = new List<object>();

			GetAllExports<object>(CreateInjectionContext(), exportName, returnValues, null);

			return returnValues;
		}

		public IEnumerable<object> LocateAllWithContext(IInjectionContext injectionContext, string exportName)
		{
			if (string.IsNullOrEmpty(exportName))
			{
				throw new ArgumentNullException("exportName", "cannot be null");
			}

			List<object> returnValues = new List<object>();

			GetAllExports<object>(injectionContext, exportName, returnValues, null);

			return returnValues;
		}

		[DefaultOverload]
		public IEnumerable<object> LocateAllByType(Type exportType, FilterExport filterMethod)
		{
			if (exportType == null)
			{
				throw new ArgumentNullException("exportType", "cannot be null");
			}

			List<object> returnValues = new List<object>();

			GetAllExports<object>(CreateInjectionContext(), exportType.FullName, returnValues, filterMethod);

			return returnValues;
		}

		public IEnumerable<object> LocateAllByType(TypeWrapper exportType, FilterExport filterMethod)
		{
			return LocateAllByType(exportType.Type, filterMethod);
		}

		public IEnumerable<object> LocateAll(string exportName, FilterExport filterMethod)
		{
			if (string.IsNullOrEmpty(exportName))
			{
				throw new ArgumentNullException("exportName", "cannot be null");
			}

			List<object> returnValues = new List<object>();

			GetAllExports<object>(CreateInjectionContext(), exportName, returnValues, filterMethod);

			return returnValues;
		}

		public Type LocateExportType(string exportName)
		{
			if (string.IsNullOrEmpty(exportName))
			{
				throw new ArgumentNullException("exportName", "cannot be null");
			}

			ExportCollection activation;

			if (activationStrategies.TryGetValue(exportName, out activation))
			{
				IExportStrategy exportStrategy = activation.ExportStrategies().FirstOrDefault();

				if (exportStrategy != null)
				{
					return exportStrategy.ActivationType;
				}
			}

			return null;
		}

		public Type LocateExportTypeByType(Type exportType)
		{
			return LocateExportType(exportType.FullName);
		}

		[DefaultOverload]
		public Type LocateExportTypeByType(TypeWrapper exportType)
		{
			return LocateExportType(exportType.Type.FullName);
		}

		public IEnumerable<Type> LocateAllExportTypes(string exportName)
		{
			List<Type> returnValue = new List<Type>();

			ExportCollection activation;

			if (activationStrategies.TryGetValue(exportName, out activation))
			{
				foreach (IExportStrategy exportStrategy in activation.ExportStrategies())
				{
					if (exportStrategy.ActivationType != null)
					{
						returnValue.Add(exportStrategy.ActivationType);
					}
				}
			}

			return returnValue;
		}

		/// <summary>
		/// Locate the type that is exporting this name.
		/// </summary>
		/// <param name="exportType"></param>
		/// <returns>
		/// export Type or null
		/// </returns>
		public IEnumerable<Type> LocateAllExportTypesByType(Type exportType)
		{
			return LocateAllExportTypes(exportType.FullName);
		}

		/// <summary>
		/// Locate the type that is exporting this name.
		/// </summary>
		/// <param name="exportName">the name to locate</param>
		/// <returns>
		/// export Type or null
		/// </returns>
		[DefaultOverload]
		public IEnumerable<Type> LocateAllExportTypesByType(TypeWrapper exportName)
		{
			return LocateAllExportTypes(exportName.Type.FullName);
		}

		/// <summary>
		/// Register all Exported types in an assembly
		/// </summary>
		/// <param name="assembly">assembly to register</param>
		/// <returns></returns>
		/// <exception cref="System.ArgumentNullException">assembly;cannot be null</exception>
		public IExportAssemblyConfiguration RegisterAssembly(object assembly)
		{
			if (assembly == null)
			{
				throw new ArgumentNullException("assembly", "cannot be null");
			}

			Assembly realAssembly = assembly as Assembly;

			if (realAssembly == null)
			{
				AssemblyWrapper wrapper = assembly as AssemblyWrapper;
				if (wrapper != null)
				{
					realAssembly = wrapper.Assembly as Assembly;
				}
				else
				{
					Type type = assembly as Type;

					if (type != null)
					{
						realAssembly = type.GetTypeInfo().Assembly;
					}
					else
					{
						TypeWrapper typeWrapper = assembly as TypeWrapper;

						if (typeWrapper != null)
						{
							realAssembly = typeWrapper.Type.GetTypeInfo().Assembly;
						}
					}
				}
			}

			if (realAssembly == null)
			{
				throw new ArgumentNullException("assembly", "must be Assembly");
			}

			IExportAssemblyConfiguration returnValue =
				new ExportAssemblyConfiguration(realAssembly, registeredTypes, strategyFactories.Keys) { Container = this };

			exportAssemblyConfigurations.Add(returnValue);

			return returnValue;
		}

		/// <summary>
		/// Adds the export configuration to the container
		/// </summary>
		/// <param name="baseExportConfiguration">The base export configuration.</param>
		/// <exception cref="System.ArgumentNullException">baseExportConfiguration</exception>
		public void AddExportConfiguration(IBaseExportConfiguration baseExportConfiguration)
		{
			if (baseExportConfiguration == null)
			{
				throw new ArgumentNullException("baseExportConfiguration");
			}

			exportConfigurations.Add(baseExportConfiguration);
		}

		public IExportConfiguration Register(Type exportType)
		{
			if (exportType == null)
			{
				throw new ArgumentNullException("exportType", "must be either Type or TypeWrapper");
			}

			IExportStrategy exportStrategy = null;

			if (exportType.GetTypeInfo().IsGenericTypeDefinition)
			{
				exportStrategy = new GenericExportStrategy(new[] { exportType.FullName }, exportType);
			}
			else
			{
				exportStrategy = new InstanceExportStrategy(exportType);
			}

			IExportConfiguration exportConfiguration =
				new ExportConfiguration(exportStrategy);

			exportConfigurations.Add(exportConfiguration);

			return exportConfiguration;
		}

		[DefaultOverload]
		public IExportConfiguration Register(TypeWrapper exportType)
		{
			return Register(exportType.Type);
		}

#if DOT_NET
		public void ScanDirectoryForExports(string directory)
		{
			foreach (string enumerateFile in Directory.EnumerateFiles(directory, "*.dll"))
			{
				try
				{
					Assembly assembly = Assembly.LoadFile(enumerateFile);

					RegisterAssembly(assembly);
				}
				catch (Exception exp)
				{
					Logger.Error("Exception thrown while trying to load assembly: " + enumerateFile, supplemental, exp);
				}
			}
		}
#endif

		public IExportConfiguration RegisterExportFunction(Type activatedType, ExportFunction exportFunction)
		{
			if (exportFunction == null)
			{
				throw new ArgumentNullException("exportFunction");
			}

			IExportConfiguration exportConfiguration =
				new ExportConfiguration(new FunctionExportStrategy(activatedType, exportFunction));

			exportConfigurations.Add(exportConfiguration);

			return exportConfiguration;
		}

		public IExportConfiguration RegisterExportJS(ExportFunction exportFunction)
		{
			return RegisterExportFunction(typeof(object), exportFunction);
		}

		public void RegisterFactory(IExportStrategyFactory newFactory)
		{
			if (newFactory == null)
			{
				throw new ArgumentNullException("newFactory");
			}

			strategyFactories.TryAdd(newFactory, true);
		}

		public void UnregisterFactory(IExportStrategyFactory oldFactory)
		{
			if (oldFactory == null)
			{
				throw new ArgumentNullException("oldFactory");
			}

			bool outValue;

			strategyFactories.TryRemove(oldFactory, out outValue);
		}

		public void BlackListExport(string exportType)
		{
			if (string.IsNullOrEmpty(exportType))
			{
				throw new ArgumentNullException("exportType");
			}

			blackList.Add(exportType);
		}

		public void BlackListExportType(Type exportType)
		{
			if (exportType == null)
			{
				throw new ArgumentNullException("exportType");
			}

			blackList.Add(exportType.FullName);
		}

		[DefaultOverload]
		public void BlackListExportType(TypeWrapper exportType)
		{
			BlackListExportType(exportType.Type);
		}

		public void ProcessRegistration()
		{
			IEnumerable<IExportStrategy> allStrategies = ProcessIActivationStrategies();

			ProcessCoreExports(CreateInjectionContext(), allStrategies, false);
		}

		public void ProcessRegistration(bool launchUI)
		{
			IEnumerable<IExportStrategy> allStrategies = ProcessIActivationStrategies();

			ProcessCoreExports(CreateInjectionContext(), allStrategies, launchUI);
		}

		public IEnumerable<IExportStrategy> GetAllStrategies()
		{
			HashSet<IExportStrategy> strategies = new HashSet<IExportStrategy>();

			foreach (var activationList in activationStrategies.Values)
			{
				foreach (IExportStrategy exportStrategy in activationList.ExportStrategies())
				{
					if (!strategies.Contains(exportStrategy))
					{
						strategies.Add(exportStrategy);
					}
				}
			}

			return strategies;
		}

		[DefaultOverload]
		public IExportStrategy GetStrategy(string name)
		{
			IExportStrategy returnValue = null;
			ExportCollection exportStrategies;

			if (activationStrategies.TryGetValue(name, out exportStrategies))
			{
				returnValue = exportStrategies.ExportStrategies().FirstOrDefault();
			}

			return returnValue;
		}

		public IExportStrategy GetStrategy(Type exportType)
		{
			IExportStrategy returnValue = null;
			ExportCollection exportStrategies;
			string name = exportType.FullName;

			if (activationStrategies.TryGetValue(name, out exportStrategies))
			{
				returnValue = exportStrategies.ExportStrategies().FirstOrDefault();
			}

			return returnValue;
		}

		[DefaultOverload]
		public IEnumerable<IExportStrategy> GetStrategies(string name)
		{
			IEnumerable<IExportStrategy> returnValue = null;
			ExportCollection exportStrategies;

			if (activationStrategies.TryGetValue(name, out exportStrategies))
			{
				returnValue = exportStrategies.ExportStrategies().ToArray();
			}

			return returnValue;
		}

		public IEnumerable<IExportStrategy> GetStrategies(Type exportType)
		{
			IEnumerable<IExportStrategy> returnValue = null;
			ExportCollection exportStrategies;
			string name = exportType.FullName;

			if (activationStrategies.TryGetValue(name, out exportStrategies))
			{
				returnValue = exportStrategies.ExportStrategies();
			}

			return returnValue;
		}

		public void AddStrategy(IExportStrategy strategy)
		{
			if (strategy == null)
			{
				throw new ArgumentNullException("strategy");
			}

			if (strategy.ActivationType == null)
			{
				throw new ArgumentException("ActivationType is null on strategy", "strategy");
			}

			strategy.Container = this;
			strategy.ActivationService = activationService;

			Logger.Debug(
				string.Format("Exporting Type {0} in environment {1} with priority {2} ",
								  strategy.ActivationType.FullName,
								  strategy.Environment,
								  strategy.Priority),
				supplemental);

			lock (activationStrategiesLock)
			{
				Dictionary<string, ExportCollection> newActivationStrategies =
					new Dictionary<string, ExportCollection>(activationStrategies);

				foreach (string activationName in strategy.ExportNames)
				{
					Logger.Debug(string.Format("Exporting Type {0} under the name {1}",
														strategy.ActivationType.FullName,
														activationName),
									 supplemental);

					ExportCollection exportCollection;

					if (newActivationStrategies.TryGetValue(activationName, out exportCollection))
					{
						exportCollection.AddExportStrategy(strategy);
					}
					else
					{
						exportCollection = new ExportCollection(this, InternalCompareValues);

						exportCollection.AddExportStrategy(strategy);

						newActivationStrategies[activationName] = exportCollection;
					}
				}

				activationStrategies = newActivationStrategies;
			}

			IExportStrategy[] newStrategyArray = new[] { strategy };

			foreach (IExportPipelineHandler exportPipelineHandler in pipelineHandlers)
			{
				IEnumerable<IExportStrategy> exportStrategies = exportPipelineHandler.FilterStrategiesForPipeline(newStrategyArray);

				if (exportStrategies != null)
				{
					foreach (IExportStrategy exportStrategy in exportStrategies)
					{
						exportStrategy.AddPiplineHandler(exportPipelineHandler);
					}
				}
			}
		}

		public void RemoveStrategy(IExportStrategy knownStrategy)
		{
			lock (activationStrategiesLock)
			{
				foreach (string activationName in knownStrategy.ExportNames)
				{
					ExportCollection exportCollection;

					if (activationStrategies.TryGetValue(activationName, out exportCollection))
					{
						exportCollection.RemoveExportStrategy(knownStrategy);
					}
				}
			}
		}

		public ExportComparison Comparision { get; set; }

		public event EventHandler<UnknownExportArgs> ResolveUnknownExport;

		private IEnumerable<IExportStrategy> ProcessAllExports()
		{
			List<IExportStrategy> returnList = new List<IExportStrategy>();

			IExportAssemblyConfiguration config;

			while (exportAssemblyConfigurations.TryTake(out config))
			{
				try
				{
					returnList.AddRange(config.GetExportStrategies());
				}
				catch (Exception exp)
				{
					Logger.Error("Exception thrown while processing assembly", supplemental, exp);
				}
			}

			IBaseExportConfiguration baseConfig;

			while (exportConfigurations.TryTake(out baseConfig))
			{
				try
				{
					returnList.Add(baseConfig.GetExportStrategy());
				}
				catch (Exception exp)
				{
					Logger.Error("Exception thrown while exporting type", supplemental, exp);
				}
			}

			return returnList;
		}

		private IEnumerable<IExportStrategy> ProcessIActivationStrategies()
		{
			lock (activationStrategiesLock)
			{
				Dictionary<string, ExportCollection> newActivationStrategies =
					new Dictionary<string, ExportCollection>(activationStrategies);
				List<IExportPipelineHandler> localHandlers = new List<IExportPipelineHandler>();
				List<IExportStrategyInspector> localInspectors = new List<IExportStrategyInspector>();
				List<IExportStrategy> sortedList =
					new List<IExportStrategy>(ProcessAllExports());

				SortActivationList(sortedList);

				if (blackList.Any())
				{
					RemoveBlackListed(sortedList);
				}

				if (activationService == null)
				{
					CreateActivationServicve(sortedList);
				}

				CreateDIExtensionExports(CreateInjectionContext(), sortedList, localInspectors, localHandlers);

				inspectors.AddRange(localInspectors);

				localHandlers.AddRange(pipelineHandlers);

				foreach (IExportPipelineHandler activationPipelineHandler in localHandlers)
				{
					IEnumerable<IExportStrategy> filteredExports = activationPipelineHandler.FilterStrategiesForPipeline(sortedList);

					if (filteredExports != null)
					{
						foreach (IExportStrategy filteredExport in filteredExports)
						{
							filteredExport.AddPiplineHandler(activationPipelineHandler);
						}
					}
				}

				pipelineHandlers = localHandlers;

				// We do this first so that when we Initialize each export it can look up it's dependencies and resolve them if they are simple
				foreach (IExportStrategy strategy in sortedList)
				{
					try
					{
						strategy.ActivationService = activationService;
						strategy.Container = this;

						foreach (string activationName in strategy.ExportNames)
						{
							Logger.Debug(string.Format("Exporting Type {0} under the name {1}",
																strategy.ActivationType.FullName,
																activationName),
											 supplemental);

							ExportCollection exportList;

							if (!newActivationStrategies.TryGetValue(activationName, out exportList))
							{
								exportList = new ExportCollection(this,InternalCompareValues);
								newActivationStrategies[activationName] = exportList;
							}

							exportList.AddExportStrategy(strategy);
						}
					}
					catch (Exception exp)
					{
						if (strategy.ActivationType != null)
						{
							Logger.Error("Exception was thrown while processing activation strategy: " +
											 strategy.ActivationType.FullName,
											 supplemental,
											 exp);
						}
						else
						{
							Logger.Error("Exception was thrown while processing activation strategy", supplemental, exp);
						}
					}
				}

				activationStrategies = newActivationStrategies;

				foreach (IExportStrategy exportStrategy in sortedList)
				{
					try
					{
						exportStrategy.Initialize();

						foreach (IExportStrategyInspector exportStrategyInspector in inspectors)
						{
							try
							{
								exportStrategyInspector.Inspect(exportStrategy);
							}
							catch (Exception exp)
							{
								Logger.Error("Exception thrown while inspecting strategy: " + exportStrategy.ActivationType.FullName,
												 supplemental,
												 exp);
							}
						}
					}
					catch (Exception exp)
					{
						Logger.Error("Exception thrown while initalizing strategy: " + exportStrategy, supplemental, exp);
					}
				}

				return sortedList;
			}
		}

		private void RemoveBlackListed(List<IExportStrategy> sortedList)
		{
			for (int i = 0; i < sortedList.Count; )
			{
				if (blackList.Contains(sortedList[i].ActivationType.FullName))
				{
					sortedList.RemoveAt(i);
				}
				else
				{
					i++;
				}
			}
		}

		private void CreateDIExtensionExports(IInjectionContext injectionContext,
														  List<IExportStrategy> sortedList,
														  List<IExportStrategyInspector> localInspectors,
														  List<IExportPipelineHandler> pipelineHandlers)
		{
			string exportResolver = typeof(IExportResolver).FullName;
			string pipelineHandler = typeof(IExportPipelineHandler).FullName;
			string exportStrategyInspector = typeof(IExportStrategyInspector).FullName;
			string defaultDisposalScopeProvider = typeof(IDefaultDisposalScopeProvider).FullName;

			foreach (IExportStrategy exportStrategy in sortedList)
			{
				if (exportStrategy.ExportNames.Contains(pipelineHandler))
				{
					exportStrategy.ActivationService = activationService;
					exportStrategy.Container = this;
					exportStrategy.Initialize();

					pipelineHandlers.Add(
						CreatePipelineExport(injectionContext, exportStrategy));
				}

				if (exportStrategy.ExportNames.Contains(exportStrategyInspector))
				{
					exportStrategy.ActivationService = activationService;
					exportStrategy.Container = this;
					exportStrategy.Initialize();

					localInspectors.Add(
						CreateStrategyInspector(injectionContext, exportStrategy));
				}

				if (exportStrategy.ExportNames.Contains(exportResolver))
				{
					CreateExternalResolver(injectionContext, exportStrategy);
				}

				if (exportStrategy.ExportNames.Contains(defaultDisposalScopeProvider))
				{
					CreateDisposalScopeProvider(injectionContext, exportStrategy);
				}
			}
		}

		private void CreateDisposalScopeProvider(IInjectionContext injectionContext, IExportStrategy exportStrategy)
		{
			if (disposalScopeProvider == null)
			{
				disposalScopeProvider = ActivateObject<IDefaultDisposalScopeProvider>(injectionContext, exportStrategy, null);
			}
		}

		private IExportStrategyInspector CreateStrategyInspector(IInjectionContext injectionContext,
																					IExportStrategy exportStrategy)
		{
			return ActivateObject<object>(injectionContext, exportStrategy, null) as IExportStrategyInspector;
		}

		private IExportPipelineHandler CreatePipelineExport(IInjectionContext injectionContext, IExportStrategy exportStrategy)
		{
			return ActivateObject<object>(injectionContext, exportStrategy, null) as IExportPipelineHandler;
		}

		private void CreateExternalResolver(IInjectionContext injectionContext, IExportStrategy exportStrategy)
		{
			try
			{
				IExportResolver resolver = ActivateObject<object>(injectionContext, exportStrategy, null) as IExportResolver;

				if (resolver != null)
				{
					externalResolvers.Add(resolver);
				}
			}
			catch (Exception exp)
			{
				Logger.Error("Exception thrown while creating external resolver", supplemental, exp);
			}
		}

		private void ProcessCoreExports(IInjectionContext injectionContext,
												  IEnumerable<IExportStrategy> allStrategies,
												  bool launchUI)
		{
			HashSet<string> exportedNames = new HashSet<string>();
			List<IExportStrategy> coreExports = new List<IExportStrategy>();

			foreach (IExportStrategy exportStrategy in allStrategies)
			{
				if (exportStrategy.IsCoreExport &&
					 (exportStrategy.CoreExportStartOrder > (int)CoreExportStartOrder.UI || launchUI))
				{
					bool export = false;

					foreach (string exportName in exportStrategy.ExportNames)
					{
						if (!exportedNames.Contains(exportName))
						{
							exportedNames.Add(exportName);
							export = true;
						}
					}

					if (export)
					{
						coreExports.Add(exportStrategy);
					}
				}
			}

			coreExports.Sort((x, y) => Comparer<int>.Default.Compare(x.CoreExportStartOrder, y.CoreExportStartOrder));

			coreExports.Reverse();

			foreach (IExportStrategy exportStrategy in coreExports)
			{
				try
				{
					ActivateObject<object>(injectionContext, exportStrategy, null);

					Logger.Debug("Activated Core Type: " + exportStrategy.ActivationType.FullName, supplemental);
				}
				catch (Exception exp)
				{
					Logger.Error("Exception thrown while creating core service", supplemental, exp);
				}
			}
		}

		private void CreateActivationServicve(List<IExportStrategy> exports)
		{
			IExportStrategy activationStrategy = null;
			IExportStrategy augmenterStrategy = null;
			Type activationServiceType = null;
			Type typeAugmenter = null;

			foreach (IExportStrategy exportStrategy in exports)
			{
				if (exportStrategy.ExportedTypes != null)
				{
					foreach (Type exportedType in exportStrategy.ExportedTypes)
					{
						if (exportedType == typeof(IActivationService) && activationService == null)
						{
							activationStrategy = exportStrategy;
							activationServiceType = exportStrategy.ActivationType;
							break;
						}

						if (exportedType == typeof(ITypeAugmenter) && augmenterStrategy == null)
						{
							augmenterStrategy = exportStrategy;
							typeAugmenter = exportStrategy.ActivationType;
							break;
						}
					}
				}
			}

			if (activationServiceType != null && activationService == null)
			{
				Logger.Info("Using Activation Service: " + activationServiceType.FullName);

				activationService =
					Activator.CreateInstance(
						activationServiceType, this) as IActivationService;

				if (activationService != null)
				{
					if (typeAugmenter != null)
					{
						activationService.TypeAugmenter =
							Activator.CreateInstance(typeAugmenter, this) as ITypeAugmenter;
					}

					activationStrategy.LifeCycleContainer = new PermanentlySharedLifeCycleContainer(activationService);
				}
			}
		}

		private void SortActivationList(List<IExportStrategy> sortedList, bool skipRemove = false)
		{
			ExportEnvironment env = Environment;

			if (!skipRemove)
			{
				sortedList.RemoveAll(
					(x) =>
					{
						if (env == ExportEnvironment.Any)
						{
							return false;
						}

						if (env == ExportEnvironment.RunTime &&
							 (x.Environment & ExportEnvironment.RunTimeOnly) != ExportEnvironment.RunTimeOnly &&
							 ((x.Environment & ExportEnvironment.UnitTestOnly) == ExportEnvironment.UnitTestOnly ||
							  (x.Environment & ExportEnvironment.DesignTimeOnly) == ExportEnvironment.DesignTimeOnly))
						{
							return true;
						}

						if (env == ExportEnvironment.UnitTest &&
							 (x.Environment & ExportEnvironment.UnitTestOnly) != ExportEnvironment.UnitTestOnly &&
							 (x.Environment == ExportEnvironment.RunTimeOnly ||
							  x.Environment == ExportEnvironment.DesignTimeOnly))
						{
							return true;
						}

						if (env == ExportEnvironment.DesignTime &&
							 (x.Environment & ExportEnvironment.DesignTimeOnly) != ExportEnvironment.DesignTimeOnly &&
							 ((x.Environment & ExportEnvironment.RunTimeOnly) == ExportEnvironment.RunTimeOnly ||
							  (x.Environment & ExportEnvironment.UnitTestOnly) == ExportEnvironment.UnitTestOnly))
						{
							return true;
						}

						return false;
					});
			}

			sortedList.Sort(InternalCompareValues);

			sortedList.Reverse();
		}

		private int InternalCompareValues(IExportStrategy x, IExportStrategy y)
		{
			if (Comparision != null)
			{
				Comparision(x, y, environment);
			}

			return CompareValues(x, y, environment);
		}

		/// <summary>
		/// This method compares 2 export strategies in a particular environment using ExportEnvironment attributes and ExportPriority attributes
		/// </summary>
		/// <param name="x"></param>
		/// <param name="y"></param>
		/// <param name="environment"></param>
		/// <returns></returns>
		public static int CompareValues(IExportStrategy x, IExportStrategy y, ExportEnvironment environment)
		{
			int returnValue = 0;

			if (x.Environment != y.Environment)
			{
				switch (environment)
				{
					case ExportEnvironment.RunTime:
						returnValue = CompareValuesInRunTime(x, y);
						break;

					case ExportEnvironment.UnitTest:
						returnValue = CompareValuesAtUnitTestTime(x, y);
						break;

					case ExportEnvironment.DesignTime:
						returnValue = CompareValuesAtDesignTime(x, y);
						break;
				}
			}

			if (returnValue == 0)
			{
				if (x.Priority > y.Priority)
				{
					returnValue = 1;
				}
				else if (x.Priority < y.Priority)
				{
					returnValue = -1;
				}
			}

			return returnValue;
		}

		private static int CompareValuesAtDesignTime(IExportStrategy x, IExportStrategy y)
		{
			int returnValue = 0;

			if ((x.Environment & ExportEnvironment.DesignTime) == ExportEnvironment.DesignTime ||
				 (x.Environment & ExportEnvironment.DesignTimeOnly) == ExportEnvironment.DesignTimeOnly)
			{
				returnValue++;
			}

			if ((y.Environment & ExportEnvironment.DesignTime) == ExportEnvironment.DesignTime ||
				 (y.Environment & ExportEnvironment.DesignTimeOnly) == ExportEnvironment.DesignTimeOnly)
			{
				returnValue--;
			}

			if (returnValue == 0)
			{
				if ((x.Environment & ExportEnvironment.Any) == ExportEnvironment.Any)
				{
					returnValue++;
				}

				if ((y.Environment & ExportEnvironment.Any) == ExportEnvironment.Any)
				{
					returnValue--;
				}
			}

			return returnValue;
		}

		private static int CompareValuesAtUnitTestTime(IExportStrategy x, IExportStrategy y)
		{
			int returnValue = 0;

			if ((x.Environment & ExportEnvironment.UnitTest) == ExportEnvironment.UnitTest ||
				 (x.Environment & ExportEnvironment.UnitTestOnly) == ExportEnvironment.UnitTestOnly)
			{
				returnValue++;
			}

			if ((y.Environment & ExportEnvironment.UnitTest) == ExportEnvironment.UnitTest ||
				 (y.Environment & ExportEnvironment.UnitTestOnly) == ExportEnvironment.UnitTestOnly)
			{
				returnValue--;
			}

			if (returnValue == 0)
			{
				if ((x.Environment & ExportEnvironment.Any) == ExportEnvironment.Any)
				{
					returnValue++;
				}

				if ((y.Environment & ExportEnvironment.Any) == ExportEnvironment.Any)
				{
					returnValue--;
				}
			}

			return returnValue;
		}

		private static int CompareValuesInRunTime(IExportStrategy x, IExportStrategy y)
		{
			int returnValue = 0;

			if ((x.Environment & ExportEnvironment.RunTime) == ExportEnvironment.RunTime
				 || (x.Environment & ExportEnvironment.RunTimeOnly) == ExportEnvironment.RunTimeOnly)
			{
				returnValue++;
			}

			if ((y.Environment & ExportEnvironment.RunTime) == ExportEnvironment.RunTime ||
				 (y.Environment & ExportEnvironment.RunTimeOnly) == ExportEnvironment.RunTimeOnly)
			{
				returnValue--;
			}

			if (returnValue == 0)
			{
				if ((x.Environment & ExportEnvironment.Any) == ExportEnvironment.Any)
				{
					returnValue++;
				}

				if ((y.Environment & ExportEnvironment.Any) == ExportEnvironment.Any)
				{
					returnValue--;
				}
			}

			return returnValue;
		}

		/// <summary>
		/// Activates an instance of T for a given Export name and constructor parameters
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="strategy"></param>
		/// <param name="constructorParams"></param>
		/// <returns></returns>
		private T ActivateObject<T>(IInjectionContext injectionContext,
													IExportStrategy strategy,
													object[] constructorParams) where T : class
		{
			return strategy.Activate(injectionContext, constructorParams) as T;
		}

		private object InternalLocate(IInjectionContext injectionContext, Type tType, object[] constructorParams = null)
		{
			object localT = injectionContext.LocateExport(tType, this, constructorParams);

			if (localT != null)
			{
				return localT;
			}

			IExportStrategy activationStrategy = null;
			ExportCollection strategies;

			// try and find a strategy that matches, if no strategy is found and it's a constructed generic
			// we are going to try and peel it apart and see if we know how to satisfy the request
			if (!activationStrategies.TryGetValue(tType.FullName, out strategies))
			{
				if (tType.IsConstructedGenericType)
				{
					// get the generic type that is the interface
					Type genericType = tType.GetGenericTypeDefinition();

					// try and solve the export as a Many return (check if IList<T> etc.
					object specialExport = ExportSpecialGenericTypes(injectionContext, tType, genericType);

					if (specialExport != null)
					{
						return specialExport;
					}

					if (CreateGenericStrategy(injectionContext, tType, genericType))
					{
						activationStrategies.TryGetValue(tType.FullName, out strategies);
					}
				}
				else if (tType.IsArray)
				{
					Type locateType = tType.GetElementType();
					Type exportStrategyType = typeof(ArrayExportStrategy<>).MakeGenericType(locateType);

					return CreateGenericManyExport(injectionContext, tType, locateType, exportStrategyType);

				}
			}

			if (strategies != null)
			{
				activationStrategy = FindExportStrategyThatMeetsConditions(injectionContext, strategies.ExportStrategies());

				// If we didn't find a something that meets condition and it's a generic type
				// Go back looking for an export that matches constraints and conditions
				if (activationStrategy == null && tType.IsConstructedGenericType)
				{
					Type genericType = tType.GetGenericTypeDefinition();

					if (CreateGenericStrategy(injectionContext, tType, genericType))
					{
						activationStrategies.TryGetValue(tType.FullName, out strategies);

						if (strategies != null)
						{
							activationStrategy = FindExportStrategyThatMeetsConditions(injectionContext, strategies.ExportStrategies());
						}
					}
				}
			}

			if (activationStrategy != null)
			{
				return ActivateObject<object>(injectionContext, activationStrategy, constructorParams);
			}

			if (tType == typeof(IDependencyInjectionContainer))
			{
				return this;
			}

			if (externalResolvers.Count > 0)
			{
				object returnValue = ExternallyResolve(tType, constructorParams);

				if (returnValue != null)
				{
					return returnValue;
				}
			}

			IEnumerable<IExportStrategy> exportStrategies = RaiseUnknownExport(tType.FullName, tType, false, injectionContext);

			if (exportStrategies != null)
			{
				IExportStrategy exportStrategy = FindExportStrategyThatMeetsConditions(injectionContext, exportStrategies);

				if (exportStrategy != null)
				{
					return ActivateObject<object>(injectionContext, exportStrategy, constructorParams);
				}
			}

			if (AutoRegisterUnknown && 
				!tType.GetTypeInfo().IsAbstract && 
				!tType.GetTypeInfo().IsInterface && 
				!tType.GetTypeInfo().IsValueType)
			{
				AttributedExportStrategy newExportStrategy = new AttributedExportStrategy(tType);

				AddStrategy(newExportStrategy);

				return ActivateObject<object>(injectionContext, newExportStrategy, constructorParams);
			}

			Logger.Info("Could not find Export strategy for: " + tType.FullName, supplemental);

			return null;
		}

		private object ExportSpecialGenericTypes(IInjectionContext injectionContext, Type tType, Type genericType)
		{
			if (genericType.GetTypeInfo().IsInterface)
			{
				if (genericType == typeof(IEnumerable<>) ||
					 genericType == typeof(IList<>) ||
					 genericType == typeof(ICollection<>))
				{
					Type locateType = tType.GenericTypeArguments[0];
					Type exportStrategyType = typeof(ListExportStrategy<>).MakeGenericType(locateType);

					return CreateGenericManyExport(injectionContext, tType, locateType, exportStrategyType);
				}

				if (genericType == typeof(IReadOnlyList<>) ||
					 genericType == typeof(IReadOnlyCollection<>))
				{
					Type locateType = tType.GenericTypeArguments[0];
					Type exportStrategyType = typeof(ReadOnlyCollectionExportStrategy<>).MakeGenericType(locateType);

					return CreateGenericManyExport(injectionContext, tType, locateType, exportStrategyType);
				}
			}
			else if (genericType == typeof(Lazy<>))
			{
				return CreateLazyObject(injectionContext, tType.GetTypeInfo().GenericTypeArguments[0]);
			}
			else if (genericType == typeof(ReadOnlyCollection<>))
			{
				Type locateType = tType.GenericTypeArguments[0];
				Type exportStrategyType = typeof(ReadOnlyCollectionExportStrategy<>).MakeGenericType(locateType);

				return CreateGenericManyExport(injectionContext, tType, locateType, exportStrategyType);
			}
			else if (genericType == typeof(List<>))
			{
				Type locateType = tType.GenericTypeArguments[0];
				Type exportStrategyType = typeof(ListExportStrategy<>).MakeGenericType(locateType);

				return CreateGenericManyExport(injectionContext, tType, locateType, exportStrategyType);
			}
			else if (typeof(IList).GetTypeInfo().IsAssignableFrom(tType.GetTypeInfo()))
			{
				IList addList = Activator.CreateInstance(tType) as IList;
				string exportName = tType.GenericTypeArguments[0].FullName;

				if (addList != null)
				{
					PopulateImportList(injectionContext, exportName, addList);

					return addList;
				}
			}
			else if (tType.FullName.StartsWith("System.Func`"))
			{
				return CreateFuncFactory(tType);
			}

			return null;
		}

		private object CreateFuncFactory(Type tType)
		{
			Type[] typeArguements = tType.GetTypeInfo().GenericTypeArguments;
			MethodInfo invokeMethod = null;

			if (typeArguements.Length == 1)
			{
				invokeMethod =
				  GetType().GetTypeInfo().GetDeclaredMethod("GenericCreateFuncFactory");

				invokeMethod = invokeMethod.MakeGenericMethod(typeArguements[0]);

			}

			if (typeArguements.Length == 2 &&
				typeArguements[0] == typeof(IInjectionContext))
			{
				invokeMethod =
				  GetType().GetTypeInfo().GetDeclaredMethod("GenericCreateFuncFactoryWithContext");

				invokeMethod = invokeMethod.MakeGenericMethod(typeArguements[1]);
			}

			if (typeArguements.Length == 3 &&
				typeArguements[0] == typeof(IInjectionContext) && typeArguements[1] == typeof(object[]))
			{
				invokeMethod =
				  GetType().GetTypeInfo().GetDeclaredMethod("GenericCreateFuncFactoryWithContextAndParams");

				invokeMethod = invokeMethod.MakeGenericMethod(typeArguements[2]);
			}

			if (invokeMethod != null)
			{
				return invokeMethod.Invoke(this, null);
			}

			Type closedType = 
				typeof(AutomaticFuncFactory<>).MakeGenericType(tType);

			IAutomaticFuncFactory automaticFuncFactory = Activator.CreateInstance(closedType) as IAutomaticFuncFactory;

			return automaticFuncFactory.CreateFactory(this);
		}

		private Func<T> GenericCreateFuncFactory<T>()
		{
			return () => (T)LocateByType(typeof(T));
		}

		private Func<IInjectionContext, T> GenericCreateFuncFactoryWithContext<T>()
		{
			return (x) => (T)LocateByTypeWithContext(x, typeof(T));
		}

		private Func<IInjectionContext, object[], T> GenericCreateFuncFactoryWithContextAndParams<T>()
		{
			return (x, y) => (T)LocateByTypeWithContext(x, typeof(T), y);
		}

		private object CreateGenericManyExport(IInjectionContext injectionContext, Type tType, Type locateType, Type exportStrategyType)
		{
			Type listType = typeof(List<>).MakeGenericType(locateType);
			Type newDelegateType = typeof(Action<,,,>).MakeGenericType(typeof(IInjectionContext),
																						  typeof(string),
																						  listType,
																						  typeof(FilterExport));

			MethodInfo closedMethod = getAllExportsMethod.MakeGenericMethod(locateType);
			Delegate newDelegate = closedMethod.CreateDelegate(newDelegateType, this);

			IExportStrategy newStrategy = Activator.CreateInstance(exportStrategyType, newDelegate) as IExportStrategy;

			if (newStrategy != null)
			{
				AddStrategy(newStrategy);

				return newStrategy.Activate(injectionContext, null);
			}

			return null;
		}

		private IExportStrategy FindExportStrategyThatMeetsConditions(IInjectionContext injectionContext, IEnumerable<IExportStrategy> strategies)
		{
			foreach (IExportStrategy tempStrategy in strategies)
			{
				if (tempStrategy.MeetsConditions(this, injectionContext))
				{
					return tempStrategy;
				}
			}

			return null;
		}

		private bool CreateGenericStrategy(IInjectionContext injectionContext, Type tType, Type genericType)
		{
			bool created = false;
			ExportCollection strategies;

			if (activationStrategies.TryGetValue(genericType.FullName, out strategies))
			{
				IExportStrategy newStrategy = null;

				foreach (IExportStrategy exportStrategy in strategies.ExportStrategies())
				{
					IGenericActivationStrategy genericStrategy = exportStrategy as IGenericActivationStrategy;

					if (genericStrategy != null && genericStrategy.MeetsConditions(this, injectionContext))
					{
						bool constraintsMatch = true;
						Type[] genericConstraints = genericStrategy.ActivationType.GetTypeInfo().GenericTypeParameters;
						Type[] invokingType = tType.GetTypeInfo().GenericTypeArguments;

						if (genericConstraints.Length != invokingType.Length)
						{
							continue;
						}

						for (int i = 0; i < genericConstraints.Length; i++)
						{
							TypeInfo realConstraintType = genericConstraints[i].GetTypeInfo().BaseType.GetTypeInfo();
							TypeInfo invokeType = invokingType[i].GetTypeInfo();

							GenericParameterAttributes parameterAttributes = genericConstraints[i].GetTypeInfo().GenericParameterAttributes;

							if (parameterAttributes == GenericParameterAttributes.ReferenceTypeConstraint &&
								 invokeType.IsValueType)
							{
								constraintsMatch = false;
								break;
							}
							if (!realConstraintType.IsAssignableFrom(invokeType))
							{
								constraintsMatch = false;
								break;
							}
						}

						if (!constraintsMatch)
						{
							continue;
						}

						try
						{
							newStrategy = genericStrategy.CreateClosedStrategy(tType);
						}
						catch (Exception exp)
						{
							Logger.Error("Exception thrown while trying to activate generic type: " + tType.FullName, supplemental, exp);
						}

						if (newStrategy != null)
						{
							lock (activationStrategiesLock)
							{
								Dictionary<string, ExportCollection> newActivationStrategies =
									new Dictionary<string, ExportCollection>(activationStrategies);

								foreach (var exportName in newStrategy.ExportNames)
								{
									ExportCollection currentList;

									if (!newActivationStrategies.TryGetValue(exportName, out currentList))
									{
										currentList = new ExportCollection(this, InternalCompareValues);

										newActivationStrategies[exportName] = currentList;
									}

									currentList.AddExportStrategy(newStrategy);
								}

								activationStrategies = newActivationStrategies;
							}

							created = true;
							break;
						}
					}
				}
			}

			return created;
		}

		private void PopulateImportList(IInjectionContext injectionContext, string exportName, IList addList)
		{
			foreach (object o in LocateAllWithContext(injectionContext, exportName))
			{
				try
				{
					addList.Add(o);
				}
				catch (Exception exp)
				{
					Logger.Error("Exception thrown while adding export to list", supplemental, exp);
				}
			}
		}

		private object CreateReadonlyCollection<T>(IInjectionContext injectionContext, string exportName)
		{
			List<T> returnList = new List<T>();

			foreach (object o in LocateAllWithContext(injectionContext, exportName))
			{
				try
				{
					returnList.Add((T)o);
				}
				catch (Exception exp)
				{
					Logger.Error("Exception thrown while adding export to list", supplemental, exp);
				}
			}

#if NETFX_CORE
			return new ReadOnlyCollection<T>(returnList);
#else
			return returnList.AsReadOnly();
#endif
		}

		private object CreateLazyObject(IInjectionContext injectionContext, Type getGenericArgument)
		{
			MethodInfo lazyGeneric = createLazyGenericMethod.MakeGenericMethod(getGenericArgument);

			return lazyGeneric.Invoke(this, new object[] { injectionContext });
		}

		private Lazy<T> CreateLazyGeneric<T>(IInjectionContext injectionContext)
		{
			return new Lazy<T>(() => (T)LocateByTypeWithContext(injectionContext, typeof(T)));
		}

		private object ExternallyResolve(Type tType, object[] constructorParams = null)
		{
			foreach (IExportResolver externalResolver in externalResolvers)
			{
				try
				{
					object returnValue = null;
					bool found;

					if (externalResolver.CanExportByType)
					{
						returnValue = externalResolver.GetExport(null, tType, out found, constructorParams);
					}
					else
					{
						returnValue = externalResolver.GetExport(tType.FullName, null, out found, constructorParams);
					}

					if (found)
					{
						return returnValue;
					}
				}
				catch (Exception exp)
				{
					Logger.Error("Exception thrown while calling external resolver", supplemental, exp);
				}
			}

			return null;
		}

		private void GetAllExports<T>(IInjectionContext injectionContext,
												string exportName,
												IList returnList,
												FilterExport filterMethod) where T : class
		{
			bool activated = false;
			T localExport = injectionContext.LocateExport(exportName, this, null) as T;

			if (localExport != null)
			{
				returnList.Add(localExport);
			}

			ExportCollection strategyList;

			if (activationStrategies.TryGetValue(exportName, out strategyList))
			{
				ActivateListOfExportStrategies<T>(injectionContext, returnList, filterMethod, strategyList.ExportStrategies());

				activated = true;
			}

			if (externalResolvers.Count > 0)
			{
				foreach (IExportResolver externalResolver in externalResolvers)
				{
					try
					{
						foreach (object export in externalResolver.GetAllExports(exportName, null))
						{
							if (export is T)
							{
								returnList.Add(export as T);
							}
						}
					}
					catch (Exception exp)
					{
						Logger.Error("Exception thrown while calling external resolver", supplemental, exp);
					}
				}
			}

			if (!activated)
			{
				IEnumerable<IExportStrategy> exportStrategies = RaiseUnknownExport(exportName, typeof(T), true, injectionContext);

				if (exportStrategies != null)
				{
					ActivateListOfExportStrategies<T>(injectionContext, returnList, filterMethod, exportStrategies);
				}
			}
		}

		private void ActivateListOfExportStrategies<T>(IInjectionContext injectionContext,
																	  IList returnList,
																	  FilterExport filterMethod,
																	  IEnumerable<IExportStrategy> strategyList) where T : class
		{
			foreach (var activationStrategy in strategyList)
			{
				bool activate = true;

				if (!activationStrategy.MeetsConditions(this, injectionContext))
				{
					continue;
				}

				if (filterMethod != null)
				{
					try
					{
						activate = filterMethod(activationStrategy);
					}
					catch (Exception exp)
					{
						activate = false;

						Logger.Error(
							"Exception thrown while filtering export skipping activation for type: " +
							activationStrategy.ActivationType.FullName,
							supplemental,
							exp);
					}
				}

				if (activate)
				{
					T returnValue =
						ActivateObject<T>(injectionContext, activationStrategy, null) as T;

					if (returnValue != null)
					{
						returnList.Add(returnValue);
					}
				}
			}
		}
	}
}